// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ProxyDefinitionTests.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Test.Robotics.Runtime
{
    using System;
    using System.Linq;
    using Microsoft.Robotics.Runtime;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Validates that proxy definitions work as expected.
    /// </summary>
    [TestClass]
    public class ProxyDefinitionTests
    {
        /// <summary>
        /// Validates proxy definition known types API.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ProxyDefinitionKnownTypesTest()
        {
            var publishMessageType = typeof(SimpleAgentMessage<int>);
            var controlMessageType = typeof(SimpleAgentMessage<float>);

            this.KnownTypesTest(publishMessageType, null);
            this.KnownTypesTest(null, controlMessageType);
            this.KnownTypesTest(publishMessageType, controlMessageType);
        }

        /// <summary>
        /// Validates proxy definition to producer and controllable.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ProxyDefinitionAsProducerAndControllableTest()
        {
            var def = this.CreateTestProxyDef(typeof(SimpleAgentMessage<int>), typeof(SimpleAgentMessage<float>));

            var producer = def.AsProducer<SimpleAgentMessage<int>>();
            Assert.IsNotNull(producer);
            Assert.AreEqual("SomeName", producer.Name);

            var controllable = def.AsControllable<SimpleAgentMessage<float>>();
            Assert.IsNotNull(controllable);
            Assert.AreEqual("SomeName", controllable.Name);
        }

        /// <summary>
        /// Validates proxy definition to producer fails for unknown type.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ProxyDefinitionAsProducerFailureTest()
        {
            var def = this.CreateTestProxyDef(typeof(SimpleAgentMessage<float>), typeof(SimpleAgentMessage<float>));
            this.AssertExceptionThrown(typeof(InvalidOperationException), () => def.AsProducer<SimpleAgentMessage<int>>());
        }

        /// <summary>
        /// Validates proxy definition to controllable fails for unknown type.
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ProxyDefinitionAsControllableFailureTest()
        {
            var def = this.CreateTestProxyDef(typeof(SimpleAgentMessage<float>), typeof(SimpleAgentMessage<float>));
            this.AssertExceptionThrown(typeof(InvalidOperationException), () => def.AsControllable<SimpleAgentMessage<int>>());
        }

        /// <summary>
        /// Create new proxy definition for testing.
        /// </summary>
        /// <param name="publishMessageType">Publish message type.</param>
        /// <param name="controlMessageType">Control message type.</param>
        /// <returns>Proxy definition</returns>
        private ProxyDefinition CreateTestProxyDef(Type publishMessageType, Type controlMessageType)
        {
            return new ProxyDefinition(
                "SomeName",
                "SomeProducer",
                "SomeHostName",
                publishMessageType == null ? null : new Type[] { publishMessageType },
                123,
                controlMessageType == null ? null : new Type[] { controlMessageType },
                456);
        }

        /// <summary>
        /// Test that proxy definitions return correct known types.
        /// </summary>
        /// <param name="publishMessageType">Type of publish messages.</param>
        /// <param name="controlMessageType">Type of control messages.</param>
        private void KnownTypesTest(Type publishMessageType, Type controlMessageType)
        {
            var def = this.CreateTestProxyDef(publishMessageType, controlMessageType);
            var types = def.GetKnownTypes();

            if (publishMessageType != null)
            {
                Assert.IsTrue(types.Contains(publishMessageType));
            }

            if (controlMessageType != null)
            {
                Assert.IsTrue(types.Contains(controlMessageType));
            }
        }

        /// <summary>
        /// Assert that particular exception is thrown upon calling given Action.
        /// </summary>
        /// <param name="expectedExceptionType">Expected exception type.</param>
        /// <param name="fn">Action to be called.</param>
        private void AssertExceptionThrown(Type expectedExceptionType, Action fn)
        {
            try
            {
                fn();
            }
            catch (Exception ex)
            {
                Assert.AreEqual<Type>(expectedExceptionType, ex.GetType());
            }
        }
    }
}
